#include "spi_dev_ctl.h"
#include "afe_cmd.h"
#include "cmds.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#define AFE_CHX_NUM_DEF AFE_CHX_NUM_8   // afe板振动通道数
#define AFE_SPDX_NUM_DEF AFE_SPDX_NUM_2 // afe板转速通道数

extern const uint8_t first_cmds[][FIRST_CMD_CODE_LENGTH];
extern const uint8_t second_cmds[][SECOND_CMD_CODE_LENGTH];
extern const uint8_t cmd_ret_table[][CMD_RET_VALID_LENGTH];

extern spi_dev_t spi1;

static int afe1_cmd_send(const uint8_t cmd_code[], uint8_t ret_code[]);
afe_dev_t afe1 = {
    .chx_num = AFE_CHX_NUM_DEF,
    .spdx_num = AFE_SPDX_NUM_DEF,
    .spi = &spi1,
    .send = afe1_cmd_send,
};

static enum cmd_exe_stat check_cmd_exe_stat(const uint8_t cmd_ret[])
{
    for (int i = 0; i < 5; i++)
    {
        if (strncmp(cmd_ret + 1, cmd_ret_table[i], 4) == 0)
            return i;
    }

    return 0;
}

static int check_sum_cmp(uint8_t data[], uint8_t chk_sum, size_t len)
{
    uint8_t sum = 0;

    for (int i = 0; i < len; i++)
        sum += data[i];
    if (sum == chk_sum)
        return 0;
    else
        return -1;
}

// 返回负值表示spi通信收发错误，检查spi通信双方协议及硬件。
// -1发送第一条命令错误；-2第一条命令返回值错误，-3发送第二条命令错误；-4第二条命令返回值错误，
// 返回0表示命令正常。
// 返回正值表示命令执行异常，应检查afe硬件。
static int spi_cmd_send(spi_dev_t *spi_dev, const uint8_t cmd_code[], uint8_t ret_code[])
{
    uint8_t cmd_ret1[FIRST_CMD_CODE_LENGTH] = {0};
    uint8_t cmd_ret2[SECOND_CMD_CODE_LENGTH] = {0};
    int ret = 0;

    // 发送指令命令码
    ret = spi_dev->transfer(cmd_code, cmd_ret1, sizeof(cmd_ret1));
    if (ret == -1)
    {
        return -1;
    }
    // 打印返回值
    if (spi_dev->dp_en)
    {
        printf("return 1 is:");
        for (int i = 0; i < sizeof(cmd_ret1); i++)
        {
            putchar(cmd_ret1[i]);
        }
    }

    // 检查返回值1  台架设备为 ： "#R******N!"
    ret = strncmp(cmd_ret1, FIRST_CMD_RET_STR, FIRST_CMD_CODE_LENGTH);
    if (ret != 0)
    {
        return -2;
    }

    // 发送返回命令码
    ret = spi_dev->transfer(second_cmds[0], cmd_ret2, sizeof(cmd_ret2));
    if (ret == -1)
    {
        return -3;
    }
    if (spi_dev->dp_en)
    {
        printf("\nreturn 2 is:");
        for (int i = 0; i < sizeof(cmd_ret2) - 2; i++)
        {
            putchar(cmd_ret2[i]);
        }
        printf(" 0x%x ", cmd_ret2[sizeof(cmd_ret2) - 2]);
        printf(" %c\n", cmd_ret2[sizeof(cmd_ret2) - 1]);
    }

    // 检查包头包尾
    if (cmd_ret2[0] != CMD_RET_CODE_HEAD || cmd_ret2[SECOND_CMD_CODE_LENGTH - 1] != CMD_RET_CODE_TAIL)
    {
        return -4;
    }
    // 检查返回值校验和
    if (check_sum_cmp(cmd_ret2 + 1, cmd_ret2[SECOND_CMD_CODE_LENGTH - 2], 4) != 0)
    {
        return -4;
    }
    // 检查指令执行状态
    ret = check_cmd_exe_stat(cmd_ret2); // 0：正常执行  1，2，3，4为对应错误码
    if (ret == exe_ok)
    {
        memcpy(ret_code, cmd_ret2 + 1, 4);
    }

    return ret;
}

static int afe1_cmd_send(const uint8_t cmd_code[], uint8_t ret_code[])
{
    // cal checksum
    uint8_t check_sum = 0;
    uint8_t cmd[FIRST_CMD_CODE_LENGTH] = {0};
    strncpy(cmd, cmd_code, FIRST_CMD_CODE_LENGTH);
    for (int i = 1; i < FIRST_CMD_CODE_LENGTH - 2; i++)
        check_sum += cmd[i];
    cmd[FIRST_CMD_CODE_LENGTH - 2] = check_sum;

    return spi_cmd_send(afe1.spi, cmd, ret_code);
}

int afe_acq_begin(afe_dev_t *afe_dev)
{
    uint8_t ret_val[4] = {0};
    return afe_dev->send(first_cmds[s_acq_begin], ret_val);
}

int afe_acq_stop(afe_dev_t *afe_dev)
{
    uint8_t ret_val[4] = {0};
    return afe_dev->send(first_cmds[s_acq_stop], ret_val);
}

int afe_cal_begin(afe_dev_t *afe_dev)
{
    uint8_t ret_val[4] = {0};
    return afe_dev->send(first_cmds[s_cal_begin], ret_val);
}

int afe_cal_stop(afe_dev_t *afe_dev)
{
    uint8_t ret_val[4] = {0};
    return afe_dev->send(first_cmds[s_cal_stop], ret_val);
}

int afe_get_fw(afe_dev_t *afe_dev, uint8_t afe_fw[])
{
    return afe_dev->send(first_cmds[g_afe_fw], afe_fw);
}

int afe_pwr_off(afe_dev_t *afe_dev)
{
    uint8_t ret_val[4] = {0};
    return afe_dev->send(first_cmds[s_pwr_off], ret_val);
}

int afe_set_chx_gain(afe_dev_t *afe_dev, uint16_t chx, pga_gain_t gain)
{
    int ret = 0;
    uint8_t ret_val[4] = {0};
    uint8_t cmd[FIRST_CMD_CODE_LENGTH] = {0};
    strncpy(cmd, first_cmds[s_pga_gain], FIRST_CMD_CODE_LENGTH);
    cmd[7] = gain;
    for (int i = 0; i < afe_dev->chx_num; i++)
    {
        if (((uint16_t)1 << i) & chx)
        {
            if (i < 10)
                cmd[6] = i + 0x30; // ch0~ch9  -->  '0'~'9'
            else
                cmd[6] = i + 0x37; // ch10~ch15  -->  'A'~'F'
            ret = afe_dev->send(cmd, ret_val);
            if (ret != 0)
                break;
        }
    }

    return ret;
}

int afe_get_chx_gain(afe_dev_t *afe_dev, uint16_t chx, pga_gain_t *gain)
{
    int ret = 0;
    uint8_t ret_val[4] = {0};
    uint8_t cmd[FIRST_CMD_CODE_LENGTH] = {0};
    strncpy(cmd, first_cmds[g_pga_gain], FIRST_CMD_CODE_LENGTH);

    for (int i = 0; i < afe_dev->chx_num; i++)
    {
        if (((uint16_t)1 << i) & chx)
        {
            if (i < 10)
                cmd[6] = i + 0x30; // ch0~ch9  -->  '0'~'9'
            else
                cmd[6] = i + 0x37; // ch10~ch15  -->  'A'~'F'
            ret = afe_dev->send(cmd, ret_val);
            break;
        }
    }

    if (ret == 0)
        *gain = ret_val[3];

    return ret;
}

int afe_set_chx_mode(afe_dev_t *afe_dev, uint16_t chx, chx_mode_t mode)
{
    int ret = 0;
    uint8_t ret_val[4] = {0};
    uint8_t cmd[FIRST_CMD_CODE_LENGTH] = {0};
    strncpy(cmd, first_cmds[s_chx_mode], FIRST_CMD_CODE_LENGTH);
    cmd[7] = mode;
    for (int i = 0; i < afe_dev->chx_num; i++)
    {
        if (((uint16_t)1 << i) & chx)
        {
            if (i < 10)
                cmd[6] = i + 0x30; // ch0~ch9  -->  '0'~'9'int
            else
                cmd[6] = i + 0x37; // ch10~ch15  -->  'A'~'F'
            ret = afe_dev->send(cmd, ret_val);
            if (ret != 0)
                break;
        }
    }

    return ret;
}

int afe_get_chx_mode(afe_dev_t *afe_dev, uint16_t chx, chx_mode_t *mode)
{
    int ret = 0;
    uint8_t ret_val[4] = {0};
    uint8_t cmd[FIRST_CMD_CODE_LENGTH] = {0};
    strncpy(cmd, first_cmds[g_chx_mode], FIRST_CMD_CODE_LENGTH);

    for (int i = 0; i < afe_dev->chx_num; i++)
    {
        if (((uint16_t)1 << i) & chx)
        {
            if (i < 10)
                cmd[6] = i + 0x30; // ch0~ch9  -->  '0'~'9'
            else
                cmd[6] = i + 0x37; // ch10~ch15  -->  'A'~'F'
            ret = afe_dev->send(cmd, ret_val);
            break;
        }
    }

    if (ret == 0)
        *mode = ret_val[3];

    return ret;
}

int afe_set_spdx_mode(afe_dev_t *afe_dev, uint16_t spdx, spdx_mode_t mode)
{
    int ret = 0;
    uint8_t ret_val[4] = {0};
    uint8_t cmd[FIRST_CMD_CODE_LENGTH] = {0};
    strncpy(cmd, first_cmds[s_spdx_mode], FIRST_CMD_CODE_LENGTH);
    cmd[7] = mode;
    for (int i = 0; i < afe_dev->spdx_num; i++)
    {
        if (((uint16_t)1 << i) & spdx)
        {
            if (i < 10)
                cmd[6] = i + 0x30; // ch0~ch9  -->  '0'~'9'int
            else
                cmd[6] = i + 0x37; // ch10~ch15  -->  'A'~'F'
            ret = afe_dev->send(cmd, ret_val);
            if (ret != 0)
                break;
        }
    }

    return ret;
}

// todo!
int afe_get_spdx_mode(afe_dev_t *afe_dev, uint16_t spdx, spdx_mode_t *mode);

int afe_set_samp_rate(afe_dev_t *afe_dev, samp_rate_t samp_rate)
{
    uint8_t ret_val[4] = {0};
    uint8_t cmd[FIRST_CMD_CODE_LENGTH] = {0};
    strncpy(cmd, first_cmds[s_afe_samp], FIRST_CMD_CODE_LENGTH);
    cmd[7] = samp_rate;

    return afe_dev->send(cmd, ret_val);
}

int afe_get_samp_rate(afe_dev_t *afe_dev, samp_rate_t *samp_rate)
{
    uint8_t ret_val[4] = {0};
    int ret = 0;

    ret = afe_dev->send(first_cmds[g_afe_samp], ret_val);

    if (ret == 0)
        *samp_rate = ret_val[3];

    return ret;
}

// todo!状态值不知道怎么定义
int afe_get_stat(afe_dev_t *afe_dev, uint8_t afe_stat[])
{
    uint8_t ret_val[4] = {0};
    int ret = 0;

    ret = afe_dev->send(first_cmds[g_afe_stat], ret_val);
    if (ret == 0)
    {
        afe_stat[0] = ret_val[0];
        afe_stat[1] = ret_val[1];
        afe_stat[2] = ret_val[2];
        afe_stat[3] = ret_val[3];
    }

    return ret;
}
